package com.yuneec.fly.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.yuneec.IPCameraManager.DES;
import com.yuneec.droneservice.model.ControllerLocation;
import com.yuneec.droneservice.parse.DroneFeedback;

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.storage.StorageManager;

public class FlightLog {

    private FileOutputStream mTelemetryFos = null;
    private FileOutputStream mRemoteFos = null;
    private FileOutputStream mRemoteGpsFos = null;
    private String telFilePath;
    private String remFilePath;
    private String remGPSFilePath;
    private static int reserveNum = 100;
    private static int maxNum = 10000;

    private static FlightLog instance = null;
    private boolean isCreatedNode = false;
    private String apppath = "sdcard";
    private Context context;

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss:SSS");

    private FlightLog() {
    }

    public static FlightLog getInstance() {
        if (instance == null) {
            instance = new FlightLog();
        }
        return instance;
    }

    public synchronized void createFlightNote(Context context) {
        this.context = context;
        isCreatedNode = true;
        if (!isSdCardExist()) {
            return;
        } else {
            String[] paths = sdCardPath(); // [/storage/sdcard0, /storage/sdcard1]
            apppath = paths[0];
        }
        if (mRemoteGpsFos != null || mRemoteFos != null || mTelemetryFos != null) {
            return;
        }
        // String apppath = getApplicationContext().getFilesDir().getAbsolutePath();
        int logFileIndex = 1;
        File blackBox = new File(apppath + "/FlightLog");
        if (!blackBox.exists()) {
            blackBox.mkdir();
        }

        // Telemetry note
        File telemtryFile = new File(apppath + "/FlightLog/Telemetry");
        if (!telemtryFile.exists()) {
            telemtryFile.mkdir();
        } else {
            delOtherFile(apppath, telemtryFile);
            String[] names = telemtryFile.list();
            for (int i = 0; i < names.length; i++) {
                if (names[i].contains("Telemetry")) {
                    String sub = names[i].substring(10, 15);
                    int nameIndex = Integer.parseInt(sub);
                    if (nameIndex > logFileIndex) {
                        logFileIndex = nameIndex;
                    }
                }
            }
            if (logFileIndex == maxNum) {
                logFileIndex = 1;
            } else {
                logFileIndex = logFileIndex + 1;
            }
        }
        delLogFile(logFileIndex, reserveNum, maxNum);
        // String logFileName = "Telemetry_" + System.currentTimeMillis()+".csv";
        String telFileName = "Telemetry_" + String.format("%05d", logFileIndex) + ".csv";
        telFilePath = apppath + "/FlightLog/Telemetry/" + telFileName;
        String title = getParamsName(DroneFeedback.class);
        try {
            mTelemetryFos = new FileOutputStream(telFilePath);
            mTelemetryFos.write(title.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Remote note
        File remoteFile = new File(apppath + "/FlightLog/Remote");
        if (!remoteFile.exists()) {
            remoteFile.mkdir();
        }
        String remFileName = "Remote_" + String.format("%05d", logFileIndex) + ".csv";
        remFilePath = apppath + "/FlightLog/Remote/" + remFileName;
        String remTitle = ",CH0,CH1,CH2,CH3,CH4,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16,CH17,CH18,CH19,CH20,CH21,CH22,CH23\n";
        try {
            mRemoteFos = new FileOutputStream(remFilePath);
            mRemoteFos.write(remTitle.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Remote GPS note
        File remoteGPSFile = new File(apppath + "/FlightLog/RemoteGPS");
        if (!remoteGPSFile.exists()) {
            remoteGPSFile.mkdir();
        }
        String remGPSFileName = "RemoteGPS_" + String.format("%05d", logFileIndex) + ".csv";
        remGPSFilePath = apppath + "/FlightLog/RemoteGPS/" + remGPSFileName;
        String remGPSTitle = getParamsName(ControllerLocation.class);
        try {
            mRemoteGpsFos = new FileOutputStream(remGPSFilePath);
            mRemoteGpsFos.write(remGPSTitle.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isSdCardExist() {
        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    }

    private String[] sdCardPath() {
        String[] paths = null;
        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            paths = (String[]) sm.getClass().getMethod("getVolumePaths", new Class[]{}).invoke(sm, new Object[]{});
            int a = paths.length;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return paths;
    }

    private void saveTelemetryFos(byte[] fosData) {
        try {
            if (mTelemetryFos != null) {
                mTelemetryFos.write(fosData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void saveTelemetryFos(DroneFeedback feedback) {
        saveTelemetryFos(getLogString(feedback).getBytes());
    }

    public void saveRemoteFos(int[] channels) {
        String timestamp = sdf.format(new Date());
        String remote = timestamp + ",";
        for (int i = 0; i < channels.length; i++) {
            remote += channels[i] + ",";
        }
        remote = remote.substring(0, remote.length() - 1) + "\n";
        saveRemoteFos(remote.getBytes());
    }

    public void saveRemoteFos(byte[] fosData) {
        try {
            if (mRemoteFos != null) {
                mRemoteFos.write(fosData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void saveRemoteGpsFos(byte[] fosData) {
        try {
            if (mRemoteGpsFos != null) {
                mRemoteGpsFos.write(fosData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void saveRemoteGpsFos(ControllerLocation location) {
        saveRemoteGpsFos(getLogString(location).getBytes());
    }

    private void delLogFile(int logFileIndex, int reserveNum, int maxNum) {
        int delLogFileIndex = logFileIndex - reserveNum;
        if (logFileIndex < reserveNum) {
            delLogFileIndex = logFileIndex - reserveNum + maxNum;
        }
        String delTelFileName = apppath + File.separator + "FlightLog"
                + File.separator + "Telemetry" + File.separator + "Telemetry_"
                + String.format("%05d", delLogFileIndex) + ".csv";
        String delRemFileName = apppath + File.separator + "FlightLog"
                + File.separator + "Remote" + File.separator + "Remote_"
                + String.format("%05d", delLogFileIndex) + ".csv";
        String delRemGPSFileName = apppath + File.separator + "FlightLog"
                + File.separator + "RemoteGPS" + File.separator + "RemoteGPS_"
                + String.format("%05d", delLogFileIndex) + ".csv";
        File fileDelTelFileName = new File(delTelFileName);
        if (fileDelTelFileName.exists()) {
            fileDelTelFileName.delete();
        }
        File fileDelRemFileName = new File(delRemFileName);
        if (fileDelRemFileName.exists()) {
            fileDelRemFileName.delete();
        }
        File fileDelRemGPSFileName = new File(delRemGPSFileName);
        if (fileDelRemGPSFileName.exists()) {
            fileDelRemGPSFileName.delete();
        }
    }

    private void delOtherFile(String apppath, File telemtryFile) {
        String[] names = telemtryFile.list();
        for (int i = 0; i < names.length; i++) {
            String fileName = names[i];
            String fileLastName = fileName.substring(10, 15);
            if (!(fileName.contains("Telemetry_") && fileLastName.matches("[0-9]+"))) {
                File file = new File(apppath + "/FlightLog/Telemetry/" + fileName);
                if (file.exists()) {
                    file.delete();
                }
            }
        }
    }


    private Runnable closeFlightModeRunnable = new Runnable() {

        @Override
        public void run() {
            if (!isCreatedNode) {
                flightNoteClose();
            }
        }
    };

    private Handler mHandler = new Handler();

    public synchronized void closedFlightNote(boolean forceClose) {
        if (forceClose) {
            flightNoteClose();
            isCreatedNode = false;
            mHandler.removeCallbacksAndMessages(null);
        } else {
            if (isCreatedNode) {
                isCreatedNode = false;
                mHandler.removeCallbacks(closeFlightModeRunnable);
                mHandler.postDelayed(closeFlightModeRunnable, 30 * 1000);
            }
        }
    }

    private void flightNoteClose() {
        if (mTelemetryFos != null) {
            try {
                mTelemetryFos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (mRemoteFos != null) {
            try {
                mRemoteFos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (mRemoteGpsFos != null) {
            try {
                mRemoteGpsFos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (mRemoteGpsFos != null || mRemoteFos != null || mTelemetryFos != null) {
            encryptLog();
        }
        mRemoteGpsFos = null;
        mRemoteFos = null;
        mTelemetryFos = null;

    }

    private void encryptLog() {
        DES.logEncrypt(telFilePath);
        telFilePath = null;
        DES.logEncrypt(remFilePath);
        remFilePath = null;
        DES.logEncrypt(remGPSFilePath);
        remGPSFilePath = null;
    }


    private String getParamsName(Class clazz) {
        if (clazz == ControllerLocation.class) {
            return (
                    ",lon"
                            + ",lat"
                            + ",alt"
                            + ",accuracy"
                            + ",speed"
                            + ",angle"
                            + "\n");
        } else if (clazz == DroneFeedback.class) {
            return (
                    ",fsk_rssi"
                            + ",voltage"
                            + ",current"
                            + ",altitude"
                            + ",latitude"
                            + ",longitude"
                            + ",tas"
                            + ",gps_used"
                            + ",fix_type"
                            + ",satellites_num"
                            + ",roll"
                            + ",yaw"
                            + ",pitch"
                            + ",motor_status"
                            + ",imu_status"
                            + ",press_compass_status"
                            + ",f_mode"
                            + ",gps_status"
                            + ",vehicle_type"
                            + ",error_flags1"
                            + ",gps_accH"
                            + "\n");
        }
        return "";
    }

    private String getLogString(Object obj) {
        String timestamp = sdf.format(new Date());
        if (obj instanceof ControllerLocation) {
            ControllerLocation location = (ControllerLocation) obj;
            return timestamp
                    + "," + location.longitude
                    + "," + location.latitude
                    + "," + location.altitude
                    + "," + location.accuracy * 10
                    + "," + location.speed * 10
                    + "," + location.angle
                    + "\n";
        } else if (obj instanceof DroneFeedback) {
            DroneFeedback feedback = (DroneFeedback) obj;
            return timestamp
                    + "," + feedback.fskRssi
                    + "," + feedback.voltage
                    + "," + feedback.current
                    + "," + feedback.altitude
                    + "," + feedback.latitude
                    + "," + feedback.longitude
                    + "," + feedback.tas
                    + "," + feedback.gpsUsed
                    + "," + feedback.fixType
                    + "," + feedback.satellitesNum
                    + "," + feedback.roll
                    + "," + feedback.yaw
                    + "," + feedback.pitch
                    + "," + feedback.motorStatus
                    + "," + feedback.imuStatus
                    + "," + feedback.pressCompassStatus
                    + "," + feedback.fMode
                    + "," + feedback.gpsStatus
                    + "," + feedback.vehicleType
                    + "," + feedback.errorFlags1
                    + "," + feedback.gpsAccH
                    + "\n";
        }
        return "";
    }
}
